package p.ithorns.tools.translator.apt.utils;

import com.sun.source.tree.Tree;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.*;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Name;
import p.ithorns.tools.translator.annotation.DictType;
import p.ithorns.tools.translator.apt.Consts;
import p.ithorns.tools.translator.apt.model.AnnotateMeta;
import p.ithorns.tools.translator.apt.model.FieldMeta;

import javax.lang.model.element.*;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import java.util.*;
import java.util.stream.Collectors;

/**
 * AptUtil
 * APT工具类
 *
 * @author HtL
 * @date 2024/6/6 15:07
 * @since 1.0.0
 */
@SuppressWarnings({"unused", "unchecked"})
public class AptUtil {

    /**
     * 从字段上获取DictMeta注解
     */
    public static JCTree.JCAnnotation getDictMeta(JCTree.JCVariableDecl varDecl) {
        List<JCTree.JCAnnotation> annotations = varDecl.getModifiers().getAnnotations();
        for (JCTree.JCAnnotation annotation : annotations) {
            String annotationFqn = AptUtil.getJcAnnotationFQN(annotation);
            if (DictType.getMetaClassNames().contains(annotationFqn)) {
                return annotation;
            }
        }
        return null;
    }

    /**
     * 构建字端元信息
     *
     * @param varDecl -JCVariableDecl
     * @return FieldMeta
     */
    public static FieldMeta buildFieldMeta(JCTree.JCVariableDecl varDecl) {
        FieldMeta meta = new FieldMeta();

        String metaName = AptUtil.getJcMemberName(varDecl);
        meta.setName(metaName);

        JCTree.JCAnnotation dictMeta = AptUtil.getDictMeta(varDecl);
        if (null != dictMeta) {
            meta.setMetaName(AptUtil.getJcAnnotationTypeName(dictMeta));
            String label = AptUtil.readJcAnnotationArg(dictMeta, "label");
            if (AptUtil.isEmpty(label)) {
                label  = metaName + "Label";
            }
            meta.setLabel(label);
        }

        Set<AnnotateMeta> assignSet = new HashSet<>();
        List<JCTree.JCAnnotation> annotations = varDecl.getModifiers().getAnnotations();
        if (null != annotations) {
            for (JCTree.JCAnnotation annotation : annotations) {
                assignSet.add(new AnnotateMeta(annotation, metaName));
            }
        }
        meta.setAssignSet(assignSet);

        return meta;
    }

    /**
     * 判断是否是合法的字段
     *
     * @param jcTree 语法树节点
     * @return 是否是合法字段
     */
    public static boolean isValidField(JCTree jcTree) {
        if (jcTree.getKind().equals(JCTree.Kind.VARIABLE)) {
            JCTree.JCVariableDecl jcVariable = (JCTree.JCVariableDecl) jcTree;

            Set<Modifier> flagSets = jcVariable.mods.getFlags();
            return (!flagSets.contains(Modifier.STATIC)
                    && !flagSets.contains(Modifier.FINAL));
        }

        return false;
    }

    /**
     * 获取字段的语法树节点的集合
     *
     * @param jcClass 类的语法树节点
     * @return 字段的语法树节点的集合
     */
    @SuppressWarnings("AlibabaLowerCamelCaseVariableNaming")
    public static List<JCTree.JCVariableDecl> getJCVariables(JCTree.JCClassDecl jcClass) {
        ListBuffer<JCTree.JCVariableDecl> jcVariables = new ListBuffer<>();

        // 遍历jcClass的所有内部节点，可能是字段，方法等等
        for (JCTree jcTree : jcClass.defs) {
            // 找出所有set方法节点，并添加
            if (isValidField(jcTree)) {
                // 注意这个com.sun.tools.javac.util.List的用法，不支持链式操作，更改后必须赋值
                jcVariables.append((JCTree.JCVariableDecl) jcTree);
            }
        }

        return jcVariables.toList();
    }

    /**
     * 判断是否存在无参构造方法
     *
     * @param jcClass 类的语法树节点
     * @return 是否存在
     */
    public static boolean hasNoArgsConstructor(JCTree.JCClassDecl jcClass) {
        for (JCTree jcTree : jcClass.defs) {
            if (jcTree.getKind().equals(JCTree.Kind.METHOD)) {
                JCTree.JCMethodDecl jcMethod = (JCTree.JCMethodDecl) jcTree;
                if (Consts.CONSTRUCTOR_NAME.equals(jcMethod.name.toString())) {
                    if (jcMethod.params.isEmpty()) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /**
     * 从字段上获取指定注解
     *
     * @param varDecl        -
     * @param annotationName -
     * @return -
     */
    public static JCTree.JCAnnotation getJcAnnotation(JCTree.JCVariableDecl varDecl,
                                                      String annotationName) {
        List<JCTree.JCAnnotation> annotations = varDecl.getModifiers().getAnnotations();
        for (JCTree.JCAnnotation annotation : annotations) {
            String annotationFqn = AptUtil.getJcAnnotationFQN(annotation);
            if (annotationName.equals(annotationFqn)) {
                return annotation;
            }
        }
        return null;
    }

    /**
     * 读取注解上的参数值
     *
     * @param annotation -注解
     * @param argName    -参数名
     * @param <T>        -值类型
     * @return 参数值
     */
    public static <T> T readJcAnnotationArg(JCTree.JCAnnotation annotation, String argName) {
        List<JCTree.JCExpression> arguments = annotation.getArguments();
        for (JCTree.JCExpression argument : arguments) {
            JCTree.JCExpression variable = ((JCTree.JCAssign) argument).getVariable();
            if (argName.equals(AptUtil.getJCExpressionValue(variable))) {
                JCTree.JCExpression expression = ((JCTree.JCAssign) argument).getExpression();
                return AptUtil.getJCExpressionValue(expression);
            }
        }
        return null;
    }

    /**
     * 读取元素指定注解的参数值
     * 注意，这里返回的<T>都不是Java类，而是Javac的类，需要自行转换
     *
     * @param element  -JCTree元素
     * @param typeName -注解类型名
     * @param argName  -参数名
     * @param <T>      -值类型
     * @return 参数值
     */
    @SuppressWarnings("unchecked")
    public static <T> T readJcAnnotationArg(Element element, String typeName, String argName) {
        AnnotationMirror am = getElementAnnotationMirror(element, typeName);
        if (am == null) {
            return null;
        }
        AnnotationValue av = getAnnotationValue(am, argName);
        return av == null ? null : (T) av.getValue();
    }

    /**
     * 读取字段上指定注解的参数值
     *
     * @param varDecl  -字段
     * @param typeName -注解类型名
     * @param argName  -参数名
     * @param <T>      -值类型
     * @return 参数值
     */
    public static <T> T readJcAnnotationArg(JCTree.JCVariableDecl varDecl, String typeName, String argName) {
        JCTree.JCAnnotation annotation = getJcAnnotation(varDecl, typeName);
        if (null == annotation) {
            return null;
        }
        return readJcAnnotationArg(annotation, argName);
    }


    /**
     * 读取元素上的注解镜像
     *
     * @param element      -元素
     * @param annoTypeName -注解类型名
     * @return AnnotationMirror
     */
    private static AnnotationMirror getElementAnnotationMirror(Element element, String annoTypeName) {
        for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) {
            if (Objects.equals(annotationMirror.getAnnotationType().toString(), annoTypeName)) {
                return annotationMirror;
            }
        }
        return null;
    }

    /**
     * 读取元素上的注解值
     *
     * @param annoMirror -注解镜像
     * @param key        -参数名
     * @return 参数值
     */
    private static AnnotationValue getAnnotationValue(AnnotationMirror annoMirror, String key) {
        Set<? extends Map.Entry<? extends ExecutableElement, ? extends AnnotationValue>> entries
                = annoMirror.getElementValues().entrySet();
        for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : entries) {
            if (Objects.equals(key, entry.getKey().getSimpleName().toString())) {
                return entry.getValue();
            }
        }
        return null;
    }


    /**
     * 获取注解的FQN
     *
     * @param annotation -注解
     * @return String
     */
    public static String getJcAnnotationFQN(JCTree.JCAnnotation annotation) {
        JCTree type = annotation.getAnnotationType();
        if (type instanceof JCTree.JCIdent jcIdent) {
            return jcIdent.sym.toString();
        }

        if (type instanceof JCTree.JCFieldAccess jca) {
            return jca.selected.toString();
        }

        return type.toString();
    }

    /**
     * 获取注解的Name
     */
    public static Name getJcAnnotationName(JCTree.JCAnnotation annotation) {
        JCTree.JCIdent jcIdent = (JCTree.JCIdent) annotation.getAnnotationType();
        return jcIdent.getName();
    }

    /**
     * 获取注解的TypeName
     */
    public static String getJcAnnotationTypeName(JCTree.JCAnnotation annotation) {
        JCTree annotationType = annotation.getAnnotationType();
        return annotationType.type.toString();
    }

    /**
     * 获取元素的FQN
     */
    public static String getQualifiedName(Element element) {
        if (element instanceof VariableElement) {
            TypeElement typeElement = (TypeElement) element.getEnclosingElement();
            return typeElement.getQualifiedName().toString();
        }

        if (element instanceof TypeElement typeElement) {
            return typeElement.getQualifiedName().toString();
        }

        return "";
    }

    /**
     * 组装字段元素的FQN
     * 这里是自定义的FQN格式： clazz#field
     */
    public static String makeVarElementFQN(Element element) {
        if (element instanceof VariableElement) {
            TypeElement typeElement = (TypeElement) element.getEnclosingElement();
            return typeElement.getQualifiedName() + Consts.TYPE_VARIABLE_SYMBOL + element.getSimpleName().toString();
        }

        if (element instanceof TypeElement typeElement) {
            return typeElement.getQualifiedName().toString();
        }

        return "";
    }

    /**
     * 组装类字段FQN
     */
    public static String makeJCVarDeclFQN(JCTree.JCClassDecl classDecl, JCTree.JCVariableDecl varDecl) {
        String classFqn = getJCClassFQN(classDecl);
        return classFqn + Consts.TYPE_VARIABLE_SYMBOL + toNameString(varDecl.getName());
    }

    public static String getJCClassFQN(JCTree.JCClassDecl classDecl) {
        return toNameString(classDecl.sym.fullname);
    }

    public static String getElementName(Element element) {
        return element.getSimpleName().toString();
    }

    public static java.util.List<? extends Element> getChildrenElements(Element element) {
        return element.getEnclosedElements();
    }

    public static Element getParentElement(Element element) {
        return element.getEnclosingElement();
    }

    public static TypeElement getTypeElement(Element element) {
        Element enclosingElement = element.getEnclosingElement();
        if (enclosingElement instanceof TypeElement) {
            return (TypeElement) enclosingElement;
        }
        // 如果无法找到对应的类声明，则返回null
        return null;
    }


    public String getName(Element element) {
        TypeMirror type = element.asType();
        return type.toString();
    }

    @SuppressWarnings("AlibabaLowerCamelCaseVariableNaming")
    public static <T> T getJCExpressionValue(Object obj) {
        if (obj instanceof JCTree.JCExpression jcObj) {
            Tree.Kind kind = jcObj.getKind();
            switch (kind) {
                // 字符串
                case STRING_LITERAL -> {
                    return (T) ((JCTree.JCLiteral) jcObj).getValue();
                }
                // 枚举
                case MEMBER_SELECT -> {

                    JCTree.JCFieldAccess jcFieldAccess = (JCTree.JCFieldAccess) jcObj;
                    return (T) jcFieldAccess.getIdentifier().toString();
                }

                case IDENTIFIER -> {
                    return (T) ((JCTree.JCIdent) jcObj).getName().toString();
                }
            }
        }

        return null;
    }

    public static JCTree.JCExpression toJcExpression(TreeMaker maker, Names names, Object obj) {
        if (obj instanceof JCTree.JCFieldAccess jca) {
            String fqn = jca.getExpression().type.toString();
            return buildClassLiteral(maker, names, fqn);
        }

        if (obj instanceof JCTree.JCExpression jce) {
            return jce;
        } else {
            return maker.Literal(obj);
        }
    }


    public static boolean isEmpty(CharSequence cs) {
        return cs == null || cs.length() == 0;
    }

    public static void throwIfAssert(boolean expression, String message) {
        if (expression) {
            throw new RuntimeException(message);
        }
    }

    public static Set<String> getJCMemberNames(JCTree.JCClassDecl jcClass) {
        List<JCTree> members = jcClass.getMembers();
        return members.stream().map(AptUtil::getJcMemberName).collect(Collectors.toSet());
    }

    public static String getJcMemberName(JCTree jc) {
        if (jc instanceof JCTree.JCClassDecl classDecl) {
            return toNameString(classDecl.name);
        }

        if (jc instanceof JCTree.JCVariableDecl valDecl) {
            return toNameString(valDecl.name);
        }

        if (jc instanceof JCTree.JCMethodDecl methodDecl) {
            return toNameString(methodDecl.getName());
        }

        return null;
    }


    public static String makeCamelName(String prefix, String original) {
        // return prefix + CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, name);
        if (null == original || original.trim().isEmpty()) {
            throw new IllegalArgumentException("Name cannot be null or empty");
        }

        // 手动实现首字母大写转换
        String transformed = original.trim().substring(0, 1).toUpperCase()
                + (original.length() > 1 ? original.substring(1) : "");

        return prefix + transformed;
    }

    private static String toNameString(Name name) {
        return null == name ? "" : name.toString();
    }

    private static JCTree.JCExpression buildClassLiteral(TreeMaker maker, Names names, String fullClassName) {
        String[] parts = fullClassName.split("\\.");
        JCTree.JCExpression current = maker.Ident(names.fromString(parts[0]));
        // 逐级嵌套构造 Select 节点
        for (int i = 1; i < parts.length; i++) {
            current = maker.Select(current, names.fromString(parts[i]));
        }

        // 添加 .class 后缀
        return maker.Select(current, names.fromString("class"));
    }

    private static List<JCTree.JCAnnotation> filterList(List<JCTree.JCAnnotation> annotations, String annotationName) {
        ListBuffer<JCTree.JCAnnotation> newAnnotations = new ListBuffer<>();
        for (JCTree.JCAnnotation ann : annotations) {
            String annotationFqn = getJcAnnotationFQN(ann);
            if (!annotationFqn.equals(annotationName)) {
                newAnnotations.append(ann);
            }
        }
        return newAnnotations.toList();
    }

    /**
     * 判断classDecl上不存在指定方法
     *
     * @param classDecl     -类定义
     * @param methodName    - 方法名
     * @param caseSensitive - 是否区分大小写
     * @param params        - 参数个数，-1表示不判断参数个数
     * @return 是/否
     */
    public static boolean notExistsMethod(JCTree.JCClassDecl classDecl, String methodName,
                                          boolean caseSensitive, int params) {
        for (JCTree def : classDecl.defs) {
            if (def instanceof JCTree.JCMethodDecl md) {
                String name = md.name.toString();
                boolean matches = caseSensitive ? name.equals(methodName) : name.equalsIgnoreCase(methodName);
                if (matches) {
                    if (params > -1) {
                        List<JCTree.JCVariableDecl> ps = md.params;
                        int minArgs = 0;
                        int maxArgs = 0;
                        if (ps != null && !ps.isEmpty()) {
                            minArgs = ps.length();
                            if ((ps.last().mods.flags & Flags.VARARGS) != 0) {
                                maxArgs = Integer.MAX_VALUE;
                                minArgs--;
                            } else {
                                maxArgs = minArgs;
                            }
                        }

                        if (params < minArgs || params > maxArgs) {
                            continue;
                        }
                    }

                    return false;
                }
            }
        }

        return true;
    }


    /**
     * 清除指定的注解
     *
     * @param type    - 类定义
     * @param annoFQN - 注解名称
     */
    public static void removeAnnotationIfNecessary(JCTree.JCClassDecl type, String annoFQN) {
        ListBuffer<JCTree.JCAnnotation> newAnnotations = new ListBuffer<>();
        List<JCTree.JCAnnotation> annotations = type.getModifiers().getAnnotations();
        for (JCTree.JCAnnotation ann : annotations) {
            String annotationFqn = getJcAnnotationFQN(ann);
            if (!annotationFqn.equals(annoFQN)) {
                newAnnotations.append(ann);
            }
        }
        type.mods.annotations = newAnnotations.toList();
    }


    /**
     * 清除指定的注解
     *
     * @param elements -Elements工具
     * @param varDecl  -字段
     * @param annoFQN  -指定要清除的注解FQN
     */
    public static void removeAnnotationIfNecessary(Elements elements, JCTree.JCVariableDecl varDecl, String annoFQN) {
        final TypeElement annotation = elements.getTypeElement(annoFQN);
        List<JCTree.JCAnnotation> annotations = varDecl.getModifiers().getAnnotations();
        ListBuffer<JCTree.JCAnnotation> collect = annotations.stream().filter(anno -> {
                    // 递归检查元注解
                    TypeElement candidate = AptUtil.getAnnotationTypeElement(elements, anno);
                    return candidate == null || !checkIfContainsAnnotations(annotation, candidate);
                })
                .collect(Collectors.toCollection(ListBuffer::new));
        varDecl.mods.annotations = collect.toList();
    }


    /**
     * 获取注解类型元素
     *
     * @param elements   -Elements工具
     * @param annotation -注解
     * @return TypeElement
     */
    public static TypeElement getAnnotationTypeElement(Elements elements, JCTree.JCAnnotation annotation) {
        String annotationFqn = AptUtil.getJcAnnotationFQN(annotation);
        return elements.getTypeElement(annotationFqn);
    }

    /**
     * 检查字段上是否包含某注解(递归)
     */
    public static boolean checkIfContainsAnnotation(Elements elements,
                                                    JCTree.JCVariableDecl varDecl,
                                                    String annotationName) {
        final TypeElement annotation = elements.getTypeElement(annotationName);
        List<JCTree.JCAnnotation> annotations = varDecl.getModifiers().getAnnotations();
        return annotations.stream().anyMatch(am -> {
            // 递归检查元注解
            TypeElement candidate = AptUtil.getAnnotationTypeElement(elements, am);
            return candidate != null && AptUtil.checkIfContainsAnnotations(annotation, candidate);
        });
    }


    /**
     * 检查类型元素上是否包含某注解元素(递归)
     *
     * @param annotation - 注解
     * @param candidate  -待检查的元素
     * @return true表示包含元注解
     */
    private static boolean checkIfContainsAnnotations(TypeElement annotation, TypeElement candidate) {
        final Set<TypeElement> visited = new HashSet<>();
        return checkIfContainsAnnotations(annotation, candidate, visited);
    }

    /**
     * 检查注解上是否包含词典元注解
     *
     * @param annotation - 元注解
     * @param candidate  -待检查的元素
     * @param visited    -是否已处理过
     * @return true表示包含元注解
     */
    private static boolean checkIfContainsAnnotations(TypeElement annotation,
                                                      TypeElement candidate,
                                                      Set<TypeElement> visited) {
        // 终止条件1：已检查过的注解跳过
        if (visited.contains(candidate)) {
            return false;
        }
        visited.add(candidate);

        // 终止条件2：找到目标注解@A
        if (candidate.equals(annotation)) {
            return true;
        }

        // 遍历元注解上的注解
        java.util.List<? extends AnnotationMirror> annotationMirrors = candidate.getAnnotationMirrors();
        for (AnnotationMirror metaAnnotation : annotationMirrors) {
            TypeElement upCandidate = (TypeElement) metaAnnotation.getAnnotationType().asElement();
            // 递归检查
            if (checkIfContainsAnnotations(annotation, upCandidate, visited)) {
                return true;
            }
        }

        return false;
    }

}